#include "StdAfx.h"
#include "M2InstanceLoader.h"
#include "M2BatchRender.h"

bool M2InstanceLoader::execute() {
	mInstLock.Acquire();
	if(mWaitingInstances.size() > 0) {
		for(ui32 i = 0; i < 20 && mWaitingInstances.size(); ++i) {
			MdxInstanceData& data = mWaitingInstances.front();
			mActiveInstances.push_back(data);
			mWaitingInstances.pop_front();
		}

		auto bufferPtr = mRenderer->getInstanceBufferPtr();
		auto buffer = *bufferPtr;
		if(buffer != NULL)
			buffer->Release();

		sD3D->CreateVertexBuffer(mActiveInstances.size() * 68, D3DUSAGE_WRITEONLY, D3DFVF_TEX4, D3DPOOL_MANAGED, bufferPtr, NULL);
		buffer = *bufferPtr;
		if(buffer == NULL)
			throw std::exception("Unable to create instance data buffer");

		void* mem = NULL;
		buffer->Lock(0, 0, &mem, 0);
		memcpy(mem, &mActiveInstances[0], 68 * mActiveInstances.size());
		buffer->Unlock();
		mRenderer->setNumInstances(mActiveInstances.size());
	}
	mInstLock.Release();
	return mWaitingInstances.size() == 0;
}

void M2InstanceLoader::updateVisibility() {
	MUTEX_LOCK(mInstLock);
	std::vector<MdxInstanceData> visibleInstances;
	// Reserve enough memory to prevent reallocations in every step. Later
	// the vector will be resized to free unused space
	visibleInstances.reserve(mInvisInstances.size() + mActiveInstances.size());
	std::list<MdxInstanceData> tmpInvis;
	bool changed = mForceUpdate;
	mForceUpdate = false;

	std::for_each(mInvisInstances.begin(), mInvisInstances.end(),
		[this, &changed, &visibleInstances, &tmpInvis](MdxInstanceData& id) {
			if(isInstanceVisible(id.ModelMatrix._41, id.ModelMatrix._42, id.ModelMatrix._43)) {
				changed = true;
				visibleInstances.push_back(id);
			} else {
				tmpInvis.push_back(id);
			}
	}
	);

	std::for_each(mActiveInstances.begin(), mActiveInstances.end(),
		[this, &changed, &visibleInstances, &tmpInvis](MdxInstanceData& id) {
			if(isInstanceVisible(id.ModelMatrix._41, id.ModelMatrix._42, id.ModelMatrix._43)) {
				visibleInstances.push_back(id);
			} else {
				changed = true;
				tmpInvis.push_back(id);
			}
	}
	);

	if(changed == false)
		return;

	// if necessary reduce the capacity previously allocated with reserve
	visibleInstances.resize(visibleInstances.size());
	mActiveInstances = visibleInstances;
	mInvisInstances = tmpInvis;
	mRenderer->setNumInstances(mActiveInstances.size());
	if(mActiveInstances.size() == 0)
		return;

	auto bufferPtr = mRenderer->getInstanceBufferPtr();
	auto buffer = *bufferPtr;
	if(buffer != NULL)
		buffer->Release();

	sD3D->CreateVertexBuffer(mActiveInstances.size() * 68, D3DUSAGE_WRITEONLY, D3DFVF_TEX4, D3DPOOL_MANAGED, bufferPtr, NULL);
	buffer = *bufferPtr;
	if(buffer == NULL)
		throw std::exception("Unable to create instance data buffer");

	void* mem = NULL;
	buffer->Lock(0, 0, &mem, 0);
	memcpy(mem, &mActiveInstances[0], 68 * mActiveInstances.size());
	buffer->Unlock();
	mRenderer->setNumInstances(mActiveInstances.size());
}

void M2InstanceLoader::removeInstance(ui32 id) {
	MUTEX_LOCK(mInstLock);
	auto itr = mActiveInstances.begin();
	for( ; itr != mActiveInstances.end(); ++itr) {
		if((*itr).instanceID == id) {
			mActiveInstances.erase(itr);
			mForceUpdate = true;
			return;
		}
	}

	auto itr2 = mInvisInstances.begin();
	for( ; itr2 != mInvisInstances.end(); ++itr2) {
		if((*itr2).instanceID == id) {
			mInvisInstances.erase(itr2);
			mForceUpdate = true;
			return;
		}
	}

	itr2 = mWaitingInstances.begin();
	for( ; itr2 != mWaitingInstances.end(); ++itr2) {
		if((*itr2).instanceID == id) {
			mWaitingInstances.erase(itr2);
			mForceUpdate = true;
			return;
		}
	}
}

bool M2InstanceLoader::isEmpty() {
	MUTEX_LOCK(mInstLock);
	return (mActiveInstances.size() == 0 && mWaitingInstances.size() == 0 && mInvisInstances.size() == 0);
}

void M2InstanceLoader::changeInstancePosition(ui32 id, const Vertex3F& position) {
	D3DXMATRIX matNew;
	D3DXMatrixTranslation(&matNew, position.x, position.y, position.z);
	MUTEX_LOCK(mInstLock);
	auto itr = mActiveInstances.begin();
	for( ; itr != mActiveInstances.end(); ++itr) {
		if((*itr).instanceID == id) {
			D3DXMATRIX& oldMat = (*itr).ModelMatrix;
			oldMat._41 = matNew._41;
			oldMat._42 = matNew._42;
			oldMat._43 = matNew._43;
			mForceUpdate = true;
			return;
		}
	}

	auto itr2 = mInvisInstances.begin();
	for( ; itr2 != mInvisInstances.end(); ++itr2) {
		if((*itr2).instanceID == id) {
			D3DXMATRIX& oldMat = (*itr2).ModelMatrix;
			oldMat._41 = matNew._41;
			oldMat._42 = matNew._42;
			oldMat._43 = matNew._43;
			mForceUpdate = true;
			return;
		}
	}

	itr2 = mWaitingInstances.begin();
	for( ; itr2 != mInvisInstances.end(); ++itr2) {
		if((*itr2).instanceID == id) {
			D3DXMATRIX& oldMat = (*itr2).ModelMatrix;
			oldMat._41 = matNew._41;
			oldMat._42 = matNew._42;
			oldMat._43 = matNew._43;
			mForceUpdate = true;
			return;
		}
	}
}